package com.meshnetics.gb.stack.core.layer;

import com.hse.application.ApplicationLayerException;
import com.meshnetics.gb.event.EventListener;
import com.meshnetics.gb.stack.core.InformationBase;
import com.meshnetics.gb.stack.core.Stack;
import com.meshnetics.gb.stack.core.event.EventDispatcher;
import com.meshnetics.gb.stack.core.event.StackConfirm;
import com.meshnetics.gb.stack.core.event.StackRequest;

import java.util.ArrayList;
import java.util.List;

/**
 * Basic implemenation of simple operations for StackLayer.
 * StackLayer-s not using StateMachine could extend this class.
 *
 * @author mmarkov
 */
public abstract class AbstractStackLayer implements StackLayer {
    /** The name of this layer. */
    protected String name;

    /** Stack which this StackLayer is a part of. */
    protected Stack stack;

    /** Upper StackLayer of this layer. */
    protected StackLayer upperLayer;

    /** Lower StackLayer-s of this layer. */
    protected List lowerLayers;

    /** Generator for generating unique IDs for StackRequest-s. */
    protected IDGenerator gen;

    /** InformationBase for this StackLayer. */
    protected InformationBase ib;

    /**
     * Constructs AbstractStackLayer having the given name and using the given StateMachine.
     *
     * @param name String name
     * @param ib InformationBase to create StackLayer
     * @param gen IDGenerator for generating unique IDs for StackRequest-s
     * @throws IllegalArgumentException if name, gen or sp is null
     */
    protected AbstractStackLayer(String name, InformationBase ib, IDGenerator gen) {
        if (name == null) {
            throw new IllegalArgumentException("name could not be null");
        } else if (ib == null) {
            throw new IllegalArgumentException("InformationBase could not be null");
        } else if (gen == null) {
            throw new IllegalArgumentException("IDGenerator could not be null");
        }
        this.name = name;
        this.gen = gen;
        this.ib = ib;
    }

    /**
     * @see StackLayer#getName()
     */
    public String getName() {
        return name;
    }

    /**
     * @see StackLayer#getEventListener()
     */
    public abstract EventListener getEventListener();

    /**
     * @see StackLayer#getEventDispatcher()
     */
    public abstract EventDispatcher getEventDispatcher();

    /**
     * @see StackLayer#getIDGenerator()
     */
    public IDGenerator getIDGenerator() {
        return gen;
    }

    /**
     * @see StackLayer#getInformationBase()
     */
    public InformationBase getInformationBase() {
        return ib;
    }

    /**
     * @see StackLayer#getUpperLayer()
     */
    public StackLayer getUpperLayer() {
        return upperLayer;
    }

    /**
     * @see StackLayer#setUpperLayer(StackLayer)
     */
    public void setUpperLayer(StackLayer layer) {
        upperLayer = layer;
    }

    /**
     * @see StackLayer#getLowerLayers()
     */
    public StackLayer[] getLowerLayers() {
        return ((lowerLayers == null) ? null : (StackLayer []) lowerLayers.toArray(new StackLayer[0]));
    }

    /**
     * @see StackLayer#addLowerLayer(StackLayer)
     */
    public void addLowerLayer(StackLayer layer) {
        if (lowerLayers == null) {
            lowerLayers = new ArrayList();
        }
        lowerLayers.add(layer);
        layer.setUpperLayer(this);
    }

    /**
     * @see StackLayer#removeLowerLayer(StackLayer)
     */
    public void removeLowerLayer(StackLayer layer) {
        if ((lowerLayers != null) && lowerLayers.remove(layer)) {
            layer.setUpperLayer(null);
        }
    }

    /**
     * @see StackLayer#setStack(com.meshnetics.gb.stack.core.Stack)
     */
    public void setStack(Stack stack) {
        this.stack = stack;
    }

    /**
     * @see StackLayer#getStack()
     */
    public Stack getStack() {
        return stack;
    }

    /**
     * @see StackLayer#start()
     */
    public abstract void start();

    /**
     * @see StackLayer#stop()
     */
    public abstract void stop();

    /**
     * @see StackLayer#execute(StackRequest)
     */
    public abstract StackConfirm execute(StackRequest req) throws ApplicationLayerException;
}
